借助 Kanzi Engine 插件,您可以扩展 Kanzi Engine 的功能并设置 Kanzi Studio 如何显示您创建的自定义内容:
在 Kanzi Engine 插件中您可以使用自定义属性类型创建自定义节点。您可以设置 Kanzi Studio 如何显示以及如何让用户与这些节点交互。
要使用自定义属性类型创建自定义节点:
... KZ_METACLASS_END() static kanzi::PropertyTypeEditorInfoSharedPtr makeEditorInfo(); ...
PropertyTypeEditorInfoSharedPtr MyPlugin::makeEditorInfo() { return PropertyTypeEditorInfoSharedPtr( KZ_PROPERTY_TYPE_EDITOR_INFO( []() -> PropertyTypeEditorInfo::AttributeDictionary { PropertyTypeEditorInfo::AttributeDictionary dict; //设置您要在 Kanzi Studio 中为您的节点使用的名称。 dict.displayName = "My Node"; //设置您要在 Kanzi Studio 创建 (Create) 菜单中使用的工具提示。 dict.tooltip = "This example custom node uses custom properties."; //添加水平对齐 (Horizontal Alignment) 和垂直对齐 (Vertical Alignment) 属性到该节点。 dict["AutomaticallyAddedProperties"] = "Node.HorizontalAlignment, Node.VerticalAlignment"; //添加状态机 (State Manager) 属性作为常用属性,用户可以通过点击该属性名旁的 进行添加。 dict["FrequentlyAddedProperties"] = "Node.StateManager"; //让用户使用添加属性 (Add Property) 上下文菜单或添加属性 (Add Properties) 窗口的上下文选项卡添加焦点范围 (Focus Scope) 属性。 dict["ContextProperties"] = "FocusManager.FocusScope"; return dict; }())); }
创建自定义属性类型及其元数据。
例如:
KZ_METACLASS_BEGIN(MyPlugin, Node3D, "CustomComponentType") KZ_METACLASS_END()
替换为
// String property static kanzi::PropertyType<kanzi::string> StringProperty; //字符串引用节点 static kanzi::PropertyType<kanzi::string> NodeRefByStringProperty; //字符串引用预设件 static kanzi::PropertyType<kanzi::string> PrefabRefByStringProperty; //共享指针引用预设件 static kanzi::PropertyType<kanzi::ResourceSharedPtr> PrefabRefBySharedPtrProperty; //共享指针引用材质 static kanzi::PropertyType<kanzi::ResourceSharedPtr> MaterialRefBySharedPtrProperty; KZ_METACLASS_BEGIN(MyPlugin, Node3D, "MyNode") //添加属性类型到类的元数据。 KZ_METACLASS_PROPERTY_TYPE(StringProperty) KZ_METACLASS_PROPERTY_TYPE(NodeRefByStringProperty) KZ_METACLASS_PROPERTY_TYPE(PrefabRefByStringProperty) KZ_METACLASS_PROPERTY_TYPE(PrefabRefBySharedPtrProperty) KZ_METACLASS_PROPERTY_TYPE(MaterialRefBySharedPtrProperty) KZ_METACLASS_END()
//创建字符串属性。
//本示例不设置编辑器。如果您不设置编辑器,Kanzi Studio
//对此属性类型使用默认编辑器。
PropertyType<string> MyPlugin::StringProperty(kzMakeFixedString("MyPlugin.String"), "", 0, false,
KZ_DECLARE_EDITOR_METADATA
(
metadata.displayName = "String";
));
//创建一个引用节点的字符串。
PropertyType<string> MyPlugin::NodeRefByStringProperty(kzMakeFixedString("MyPlugin.NodeRefByString"), "", 0, false,
KZ_DECLARE_EDITOR_METADATA
(
metadata.displayName = "Node Reference by String";
// 您将 valueProvider 设为 ProjectObject:Node3D 时,
// Kanzi Studio 会自动使用 Node 3D 选择器编辑器。
metadata.valueProvider = "ProjectObject:Node3D";
));
//创建引用预设件资源的字符串。
PropertyType<string> MyPlugin::PrefabRefByStringProperty(kzMakeFixedString("MyPlugin.PrefabRefByString"), "", 0, false,
KZ_DECLARE_EDITOR_METADATA
(
metadata.displayName = "Prefab Reference by String";
// 您将 valueProvider 设为 ProjectObject:PrefabTemplate 时,
// Kanzi Studio 会自动使用 Prefab 模板选择器编辑器。
metadata.valueProvider = "ProjectObject:PrefabTemplate";
));
//创建引用预设件资源的共享指针。
PropertyType<ResourceSharedPtr> MyPlugin::PrefabRefBySharedPtrProperty(kzMakeFixedString("MyPlugin.PrefabRefBySharedPtr"), ResourceSharedPtr(), 0, false,
KZ_DECLARE_EDITOR_METADATA
(
metadata.displayName = "Prefab Reference by Shared Pointer";
//您将 valueProvider 设为 ProjectObject:PrefabTemplate 时,
// Kanzi Studio 会自动使用 Prefab 模板选择器编辑器。
metadata.valueProvider = "ProjectObject:PrefabTemplate";
));
//创建引用材质资源的共享指针。
PropertyType<ResourceSharedPtr> MyPlugin::MaterialRefBySharedPtrProperty(kzMakeFixedString("MyPlugin.MaterialRefBySharedPtr"), ResourceSharedPtr(), 0, false,
KZ_DECLARE_EDITOR_METADATA
(
metadata.displayName = "Material Reference by Shared Pointer";
//您将 valueProvider 设为 ProjectObject:Material 时,
// Kanzi Studio 会自动使用 Material 下拉编辑器。
metadata.valueProvider = "ProjectObject:Material";
));
metadata.host
特性。请参阅 host。您可以创建和配置用于向应用程序添加功能的自定义消息类型。例如,您可以创建自定义触发器和动作,将属性设置为自定义节点。如果您使用 Kanzi Connect,则可以使用自定义消息类型获取您应用程序使用的 Kanzi Connect 服务器或服务相关的信息。
在 Kanzi Engine 插件中,您可以创建一个自定义消息类型,并在 Kanzi Studio 中作为触发器显示。
例如,要允许 Kanzi Studio 用户添加一个名为“On Fuel Level Changed”的触发器,触发条件是名为“Fuel Indicator”的自定义节点油量变化,请执行以下代码:
//代表 FuelIndicator.OnFuelLevelChanged 消息类型的消息参数。 class OnFuelLevelChangedMessageArguments : public MessageArguments { public: KZ_MESSAGE_ARGUMENTS_METACLASS_BEGIN(OnFuelLevelChangedMessageArguments, MessageArguments, "On Fuel Level Changed Message Arguments") KZ_METACLASS_END() }; //定义 FuelIndicator.OnFuelLevelChanged 消息的消息类型。 static MessageType<OnFuelLevelChangedMessageArguments> OnFuelLevelChangedMessage;
FuelIndicator.OnFuelLevelChanged
消息://发送一条 FuelIndicator.OnFuelLevelChanged 消息。 void onFuelLevelChanged(int fuelLevel) { OnFuelLevelChangedMessageArguments messageArguments; dispatchMessage(OnFuelLevelChangedMessage, messageArguments); }
FuelIndicator.OnFuelLevelChanged
消息的元数据。MessageType<FuelIndicator::OnFuelLevelChangedMessageArguments> FuelIndicator::OnFuelLevelChangedMessage(kzMakeFixedString("Message.FuelIndicator.OnFuelLevelChanged"), KZ_DECLARE_EDITOR_METADATA ( //设置希望该消息名称在 Kanzi Studio 中的显示方式。 metadata.displayName = "On Fuel Level Changed"; //设置消息的工具提醒。 metadata.tooltip = "Fuel Indicator sets off this trigger when its level changes."; //设置此消息列示在触发器列表的哪个类别下。 metadata.category = "Custom Controls"; //不要在触发器动作下拉列表中显示此消息。 metadata["Sendable"] = "False"; ));
在 Kanzi Engine 插件中,您可以创建自定义消息,并在 Kanzi Studio 中作为动作显示。
例如,要允许 Kanzi Studio 用户向触发器添加一个名为“Update Level”的动作,该动作显示 3D 文本块 (Text Block 3D) 节点中名为“Fuel Indicator”的自定义节点的级别,请执行以下操作:
//代表 FuelIndicator.UpdateLevel 消息的消息参数。
class FuelIndicatorUpdateLevelMessageArguments : public MessageArguments
{
public:
KZ_MESSAGE_ARGUMENTS_METACLASS_BEGIN(FuelIndicatorUpdateLevelMessageArguments, MessageArguments, "Fuel Indicator Update Level Message Arguments")
KZ_METACLASS_PROPERTY_TYPE(FuelIndicatorLevelProperty)
KZ_METACLASS_END()
//代表 Fuel Indicator 节点值的属性类型。
static PropertyType<int> FuelIndicatorLevelProperty;
//返回油位提示消息参数的值。
int getFuelIndicatorLevel()
{
return getArgument(FuelIndicatorLevelProperty);
}
};
//FuelIndicator.UpdateLevel 消息的消息类型。
static MessageType<FuelIndicatorUpdateLevelMessageArguments> FuelIndicatorUpdateLevelMessage;
FuelIndicator.UpdateLevel
消息的消息处理程序://FuelIndicator.UpdateLevel 消息的消息处理程序。 //显示 Fuel Indicator 在 3D 文本块 (Text Block 3D) 节点中的级别。 void fuelIndicatorUpdateLevelHandler(FuelIndicatorUpdateLevelMessageArguments& arguments) { //获取 Fuel Indicator 节点的级别。 m_currentLevel += arguments.getFuelIndicatorLevel(); //显示 3D 文本块 (Text Block 3D) 节点在 Fuel Indicator 节点中的级别。 TextBlock3DSharedPtr textBlock = m_textBlockReference.get(); if (textBlock) { textBlock->setText(to_string(m_currentLevel)); } }
initialize()
函数的 hpp 文件中,注册 FuelIndicator.UpdateLevel
消息的消息处理程序:void initialize() { ... //注册 FuelIndicator.UpdateLevel 消息的消息处理程序。 addMessageHandler(FuelIndicatorUpdateLevelMessage, this, &FuelIndicator::fuelIndicatorUpdateLevelHandler); }
FuelIndicator.UpdateLevel
消息的元数据。MessageType<FuelIndicator::FuelIndicatorUpdateLevelMessageArguments> FuelIndicator::FuelIndicatorUpdateLevelMessage(kzMakeFixedString("Message.FuelIndicator.UpdateLevel"), KZ_DECLARE_EDITOR_METADATA ( //将消息名称设置为要在 Kanzi Studio 中显示的方式。 metadata.displayName = "Update Level"; //在动作下拉菜单中显示此消息。 metadata["Listenable"] = "False"; ));
节点组件是您在 Kanzi Engine 插件中实现的独立逻辑片段,可以附加到任何节点,从而为该节点添加自定义功能。例如,每当一个属性值更改时,您可以将该属性值打印到 Kanzi 日志 (Log),或是定义速度可变的动画回放。
例如,创建一个名为“Log Property Value”的自定义节点组件,每当某个 float 属性类型的值更改时,将该值打印到 Kanzi 日志 (Log)。
#包含 <kanzi/core.ui/application/performance_info.hpp>
class LogPropertyValueComponent; typedef kanzi::shared_ptr<LogPropertyValueComponent> LogPropertyValueComponentSharedPtr; //代表 Log Property Value 自定义节点组件。 class LOGPROPERTYVALUECOMPONENT_API LogPropertyValueComponent : public kanzi::NodeComponent { public: //设置 Log Property Value 自定义节点组件记录哪种属性的属性类型。 static kanzi::PropertyType<kanzi::string> LoggedPropertyTypeProperty; KZ_METACLASS_BEGIN(LogPropertyValueComponent, NodeComponent, "LogPropertyValueComponent") KZ_METACLASS_PROPERTY_TYPE(LoggedPropertyTypeProperty); KZ_METACLASS_END() static kanzi::PropertyTypeEditorInfoSharedPtr makeEditorInfo(); //创建一个 LogPropertyValueComponent 节点组件实例。 static LogPropertyValueComponentSharedPtr create(kanzi::Domain* domain, kanzi::string_view name); protected: //LogPropertyValueComponent 节点组件的构建程序。 explicit LogPropertyValueComponent(kanzi::Domain* domain, kanzi::string_view name); void initialize(); //当 Log Property Value 自定义节点组件附加到节点树时,调用 attachOverride() 函数。 //在 attachOverride() 函数中,获取想要记录和添加通知处理程序(该属性值更改时进和追踪)的 //属性类型。 void attachOverride() KZ_OVERRIDE; //当 Log Property Value 自定义节点组件从节点树分离时,调用 detachOverride() 函数。 //在 detachOverride() 函数中移除跟踪要记录的属性类型值的 //更改时间的通知处理程序。 void detachOverride() KZ_OVERRIDE; ... };
//代表 Log Property Value 自定义节点组件。 class LOGPROPERTYVALUECOMPONENT_API LogPropertyValueComponent : public kanzi::NodeComponent { ... private: //Log Property Value 自定义节点组件记录的属性类型的通知处理程序。 static void onLoggedPropertyChanged(kanzi::PropertyObject& object, const float& propertyValue, kanzi::PropertyNotificationReason reason, void* owner); //记录特定属性值。 void logPropertyValue(const float& propertyValue) const; //Log Property Value 自定义节点组件记录的属性类型。 kanzi::AbstractPropertyType m_loggedPropertyType; ... };
create()
函数,并定义在头文件中实现的 attachOverride()
和 detachOverride()
回调函数://创建一个 Log Property Value 自定义节点组件。 LogPropertyValueComponentSharedPtr LogPropertyValueComponent::create(Domain* domain, string_view name) { LogPropertyValueComponentSharedPtr component(new LogPropertyValueComponent(domain, name)); component->initialize(); return component; } LogPropertyValueComponent::LogPropertyValueComponent(Domain* domain, string_view name) : kanzi::NodeComponent(domain, name) { } void LogPropertyValueComponent::initialize() { //初始化基类。 NodeComponent::initialize(); } void LogPropertyValueComponent::attachOverride() { NodeComponent::attachOverride(); //获取要记录的属性类型的名称。 const string loggedPropertyTypeName = getProperty(LoggedPropertyTypeProperty); if (loggedPropertyTypeName.empty()) { kzLogInfo(KZ_LOG_CATEGORY_GENERIC, ("Use the Logged Property Type property to set which property type you want to log.")); return; } //获取要记录的属性类型并检查其是否有效。 const AbstractPropertyType loggedPropertyType = AbstractPropertyType(getProperty(LoggedPropertyTypeProperty)); if (!loggedPropertyType) { kzLogWarning(KZ_LOG_CATEGORY_GENERIC, ("The custom node component cannot find the property type.")); return; } else if (loggedPropertyType.getDataType() != PropertyDataTypeFloat) { kzLogWarning(KZ_LOG_CATEGORY_GENERIC, ("The custom node component supports property types with float data type.")); return; } //添加要记录的属性类型的通知处理程序。 const DynamicPropertyType<float> concreteLoggedPropertyType(loggedPropertyType); getNode()->addPropertyNotificationHandler(concreteLoggedPropertyType, &LogPropertyValueComponent::onLoggedPropertyChanged, this); //将要记录的属性类型存储为数字变量。 m_loggedPropertyType = loggedPropertyType; //打印要记录的属性类型的初始值。 const optional<float> initialPropertyValue = getNode()->getOptionalProperty(concreteLoggedPropertyType); if (initialPropertyValue) { logPropertyValue(*initialPropertyValue); } } void LogPropertyValueComponent::detachOverride() { //移除要记录的属性的属性通知处理程序。 if (m_loggedPropertyType) { const DynamicPropertyType<float> concreteLoggedPropertyType(m_loggedPropertyType); getNode()->removePropertyNotificationHandler(concreteLoggedPropertyType, &LogPropertyValueComponent::onLoggedPropertyChanged, this); } NodeComponent::detachOverride(); }
void LogPropertyValueComponent::onLoggedPropertyChanged(PropertyObject& /*object*/, const float& propertyValue, PropertyNotificationReason reason, void* owner) { //获取组件, //该组件在您要记录的属性值发生变化时会接收到通知。 const LogPropertyValueComponent* component = static_cast<LogPropertyValueComponent*>(owner); //每当属性类型值发生变化或该属性类型被添加到节点时,Kanzi 都会发送通知。 component->logPropertyValue(propertyValue); } //使用 kzLogInfo 日志宏打印以下内容: // - 包含 Log Property Value 自定义节点组件的节点名称 // - 要记录的属性类型的名称 // - 该属性类型的值 void LogPropertyValueComponent::logPropertyValue(const float& propertyValue) const { kzLogInfo(KZ_LOG_CATEGORY_GENERIC, ("{}:{} = {}", getNode()->getName(), string(m_loggedPropertyType.getName()), to_string(propertyValue))); }
//声明用于设置 Log Property Value 自定义节点组件记录 //哪些属性的 Logged Property Type 属性类型的元数据。 PropertyType<string> LogPropertyValueComponent::LoggedPropertyTypeProperty(kzMakeFixedString("LogPropertyValueComponent.LoggedPropertyType"), "", 0, false, KZ_DECLARE_EDITOR_METADATA ( metadata.displayName = "Logged Property Type"; metadata.tooltip = "The property type whose value you want the node component to log."; metadata.valueProvider = "PropertyType"; )); //创建 Log Property Value 自定义节点组件的元数据。 PropertyTypeEditorInfoSharedPtr LogPropertyValueComponent::makeEditorInfo() { return PropertyTypeEditorInfoSharedPtr( KZ_PROPERTY_TYPE_EDITOR_INFO([]() -> PropertyTypeEditorInfo::AttributeDictionary { PropertyTypeEditorInfo::AttributeDictionary dict; dict.displayName = "Log Property Value Component"; dict.tooltip = "Writes the value of a specified property to Kanzi 日志 (Log) every time that property changes."; dict.category = "Custom"; return dict; }())); }
Domain
类的函数的头文件。#包含 <kanzi/core.ui/domain/performance_info.hpp>
要向 Kanzi Studio 传递有关您在 Kanzi Engine 插件中创建的自定义属性类型的信息,应声明描述这些属性类型的元数据。元数据使 Kanzi Engine 插件用户可以与 Kanzi Studio 中的插件内容交互。请参阅 显示 Kanzi Studio 中的 Kanzi Engine 插件自定义类型的参考。
例如,要为自定义属性类型 VideoFileNameProperty 创建元数据以允许 Kanzi Studio 用户选择某个视频文件:
PropertyType<string> VideoView2D::VideoFileNameProperty(kzMakeFixedString("VideoView2D.VideoFileName"), "video.mp4", 0, false, KZ_DECLARE_EDITOR_METADATA ( //按您要在 Kanzi Studio 中显示的方式设置属性类型名称。 metadata.displayName = "Video Filename"; //设置属性类型的工具提示。 metadata.tooltip = "名称 (NameVideo file to be played"; //设置在 属性 (Properties) 中列出的属性类型所属的类别。 metadata.category = "Video"; //设置您希望用户用于编辑属性类型值的编辑器。 //BrowseFileTextEditor 编辑器含有一个文本框,旁边有一个浏览 (Browse) 按钮。 metadata.editor = "BrowseFileTextEditor"; //设置 Kanzi Studio 中该属性的默认值。 //在本示例的第一行中设置 Kanzi Engine 默认值。 metadata["DefaultValue"] = "video.mp4"; ));
当用户添加新的 VideoView2D 节点时,Kanzi Studio 会在 Video 类别中的节点属性中添加 Video Filename 属性,其中有带 浏览 (Browse) 按钮的文本编辑器、工具提示,默认值设置为 video.mp4。
如果不为节点类型设置图标,Kanzi Studio 使用默认图标 ()。要使 Kanzi Studio 用户更轻松地以可视化方式跟踪工程 (Project) 中的节点,您可以为节点设置自己的图标。
要为您的节点类型设置自定义图标:
有关自定义节点类型使用自己图标的示例,请参阅 Node2D 插件示例。